#include <qqtlongdataprotocol.h>

//#define LOCALDEBUG
#ifdef LOCALDEBUG
#define p4line() p3line()
#else
#define p4line() QNoDebug()
#endif

#define OPEN_LASTWORD 1
#define OPEN_TRANSLATE_BUFFER 0
#define OPEN_ENCODE 1
#define OPEN_COMPRESS 0
#define OPEN_PROGRESS 0

/**
 * OPEN_TRANSLATE_BUFFER 1
 * 如果开启，发送的总的数据量包括16个额外的字节，
 * 如果关闭，发送Buffer可以设置到很大，发送的总的数据量没有限制，
 * 建议使用LongDataProtocol发送64KB以下数量的数据。
 *
 * OPEN_ENCODE 1
 * 发送中文，为了不乱码，必开，
 * 发送二进制，用户一定要把自己的二进制数据手动转BASE64或HEX，否则字典转json不识别！和内部转码无关。
 *
 * OPEN_LASTWORD 1
 * 在最后一个大包后追加小包，这个建议打开，可以防止发送小数据。
 *
 * OPEN_COMPRESS 0
 * 数据在发送中被压缩。
 * 如果关闭则不压缩。
 * 用户在发送数据的时候一般自己决定是否压缩数据，基础数据转Base64体积还是比较大的。
 *
 * OPEN_PROGRESS 0
 * 打开则上报进度，
 * 影响效率，上报的信号也出现了合并，所以，建议关闭。
 */

#if OPEN_ENCODE
#else
//#include <QTextCodec>
#endif

QQtLongDataProtocol::QQtLongDataProtocol ( QObject* parent )
    : QQtProtocol ( parent )
{
    mRecvFlag = false;

    setUUidWordLength();
    setSendBufferSize();
    setAppendLastWord();
    setAppendLastWordSize();
    setTranslateDataEncoding();
    setCompressTranslateData();

    m_timer = new QTimer ( this );
    m_timer->setSingleShot ( false );
    setTimerInterval();
    connect ( m_timer, SIGNAL ( timeout() ),
              this, SLOT ( slotTimeout() ) );
    m_timer->start();

    m_CleanTimer = new QTimer ( this );
    m_CleanTimer->setSingleShot ( false );
    setCleanTimerInterval();
    connect ( m_CleanTimer, SIGNAL ( timeout() ),
              this, SLOT ( slotCleanTimeout() ) );
    setAutoCleanDataCache();
}

QQtLongDataProtocol::~QQtLongDataProtocol() {}

void QQtLongDataProtocol::setSendBufferSize ( int len )
{
    mBufferSize = len;
}

int QQtLongDataProtocol::getSendBufferSize()
{
    return mBufferSize;
}

void QQtLongDataProtocol::setUUidWordLength ( int len )
{
    mUUidLen = len;
}

int QQtLongDataProtocol::getUUidWordLength()
{
    return mUUidLen;
}

void QQtLongDataProtocol::setAppendLastWord ( bool open )
{
    mAppendLastWord = open;
}

bool QQtLongDataProtocol::getAppendLastWord()
{
    return mAppendLastWord;
}

void QQtLongDataProtocol::setAppendLastWordSize ( int size )
{
    mAppendLastWordSize = size;
}

bool QQtLongDataProtocol::getAppendLastWordSize()
{
    return mAppendLastWordSize;
}

void QQtLongDataProtocol::setTranslateDataEncoding ( TranslateDataEncoding type )
{
    mTranslateDataEncode = type;
}

QQtLongDataProtocol::TranslateDataEncoding QQtLongDataProtocol::getTranslateDataEncoding()
{
    return mTranslateDataEncode;
}

void QQtLongDataProtocol::setCompressTranslateData ( QQtLongDataProtocol::CompressMethod method )
{
    mCompressMethod = method;
}

QQtLongDataProtocol::CompressMethod QQtLongDataProtocol::getCompressTranslateData()
{
    return mCompressMethod;
}

void QQtLongDataProtocol::setTimerInterval ( int millsecond )
{
    m_timer->setInterval ( millsecond );
}

int QQtLongDataProtocol::timerInterval()
{
    return m_timer->interval();
}

void QQtLongDataProtocol::setCleanTimerInterval ( int millsecond )
{
    m_CleanTimer->setInterval ( millsecond );
}

int QQtLongDataProtocol::cleanTimerInterval()
{
    return m_CleanTimer->interval();
}

void QQtLongDataProtocol::setAutoCleanDataCache ( bool open )
{
    if ( open )
        m_CleanTimer->start();
    else
        m_CleanTimer->stop();
}

void QQtLongDataProtocol::sendMessage ( const QQtLongDataSerialization& data )
{
    //已经序列化好buffer
    protectedSendMessage ( data );
}

void QQtLongDataProtocol::recvMessage ( const QQtLongDataSerialization& data )
{
    //已经反序列化好buffer
    emit notifyToProtocolManager ( this, &data );
    emit readyRead ( data );
}

/**
 * @brief QQtLongDataProtocol::protectedSendMessage
 *
 * 内部数据结构，用户不可见。
 * [0]  "uuid"
 *      "count"
 *      "index"
 *      "status" = ready
 *      "timestamp"
 *      "totalsize"
 *      "size"
 *      "data" <1024 ---- >64KB
 *
 * 用户数据结构，自行定义。
 */
void QQtLongDataProtocol::protectedSendMessage ( const QQtLongDataSerialization& data )
{
    //data split to 1024 & write all
#if 0
    QByteArray bytes = data.buffer();
    write ( bytes );
#else

#if OPEN_TRANSLATE_BUFFER
    QQtLongDataSerialization ds0 = data;
    //本地化，提供一次序列化，防止用户漏掉序列化函数
    ds0.serialize(); //序列化好
#else
    const QQtLongDataSerialization& ds0 = data;
    //ds0.serialize(); //序列化好 buffer闲置。
#endif
    //ds0.dump();
    //ds0.dump_dictionary();

    //QQtLongDataSerialization ds1 = data;
    //ds1.dump();

#if OPEN_TRANSLATE_BUFFER
    QByteArray json_data = ds0.buffer();
#else
    QByteArray json_data = ds0.dictionary().toJson ( QJsonDocument::Compact );
#endif

#if OPEN_ENCODE
    switch ( mTranslateDataEncode )
    {
        case BASE64_DATA:
            json_data = json_data.toBase64();
            break;
        case HEX_DATA:
            json_data = json_data.toHex();
            break;
        case RAW_DATA:
        default:
            break;
    }
#else
    //这个经过测试不行，拆包后还是末尾、开头乱码，本来就是UNICODE....
    //json_data = QTextCodec::codecForName ( "UTF8" )->toUnicode ( json_data ).toLocal8Bit();
#endif

#if OPEN_COMPRESS
    switch ( mCompressMethod )
    {
        case GZipCompress:
        case ZlibCompress:
            json_data = qCompress ( json_data );
            break;
        case NoCompress:
        default:
            break;
    }
#endif

    int count = json_data.size() / mBufferSize;
    if ( json_data.size() % mBufferSize > 0 )
        count += 1;

#if OPEN_LASTWORD //last <= 100
    if ( mAppendLastWord )
    {
        if ( json_data.size() % mBufferSize <= mAppendLastWordSize )
            if ( count > 1 )
                count -= 1;
    }
#endif

    p4line() << "totalsize:" << json_data.size();
    //p4line() << qPrintable ( json_data );

    int totalsize = json_data.size();

#if OPEN_PROGRESS
    int sendsize = 0;
#endif

    QUuid quuid = QUuid::createUuid();
    QString uuid = quuid.toString().left ( mUUidLen ).mid ( 1 );

    qint64 timestamp = QDateTime::currentMSecsSinceEpoch() / 1000;

    int index = 0;
    while ( !json_data.isEmpty() )
    {
        QByteArray bytes;
        if ( json_data.size() >= mBufferSize )
            bytes.resize ( mBufferSize ); //分包大小
        else
            bytes.resize ( json_data.size() );

        json_data >> bytes;

#if OPEN_LASTWORD //last <= 100
        if ( mAppendLastWord )
        {
            if ( json_data.size() > 0 && json_data.size() <= mAppendLastWordSize )
            {
                bytes.append ( json_data );
                json_data.clear();
            }
        }
#endif
        int size = bytes.size();

        QQtLongDataSerialization ds0;
        ds0[0]["uuid"] = uuid;
        ds0[0]["count"] = count;
        ds0[0]["index"] = index++;
        ds0[0]["status"] = "ready";
        ds0[0]["timestamp"] = timestamp;
        ds0[0]["totalsize"] = totalsize;
        ds0[0]["size"] = size;
        ds0[0]["data"] = bytes;
        ds0.serialize();
        p4line() << uuid << count << index << size << totalsize << ds0.buffer().size();
        //p4line() << qPrintable ( bytes );

        QByteArray bytes1 = ds0.buffer();
        write ( bytes1 );

#if OPEN_PROGRESS
        sendsize += size;
        updateSendBytes ( sendsize, totalsize );
        if ( totalsize > 0 )
        {
            int progress = qRound ( 100.0 * sendsize / totalsize );
            updateSendProgress ( progress );
        }
#endif
        //提高发送成功率。
        //UDP：此处由于某些原因，发送成功率不高。添加阻塞可以提高成功率，这怎么可以！
        QQtSleep ( 1 );
    }
#endif
}

void QQtLongDataProtocol::protectedRecvMessage ( const QQtLongDataSerialization& data )
{
    //push_front & pop_back ?

    //针对uuid保存数据，uuid里面的index够数，便开始组合包
    //如果丢包，根据timestamp，超时删除

    const QQtLongDataSerialization& ds0 = data;

    QString uuid = ds0[0]["uuid"].getValue().toString();
    int count = ds0[0]["count"].getValue().toInt();
    int index = ds0[0]["index"].getValue().toInt();
    int totalsize = ds0[0]["totalsize"].getValue().toInt();
    int size = ds0[0]["size"].getValue().toInt();
    //qint64 timestamp = ds0[0]["timestamp"].getValue().toLongLong();

    mDataCache[uuid][index] = ds0[0];

#if 0
    int realsize = ds0[0]["data"].getValue().toByteArray().size();
    int reallength = ds0[0]["data"].getValue().toByteArray().length();
    int bufferlen = ds0.buffer().size();
    p4line() << uuid << count << index + 1 << mDataCache[uuid].count()
             << size << totalsize << realsize << reallength << bufferlen;
    //qDebug() << ds0[0];
#endif

    //全部到达，才允许结合包。
    if ( mDataCache[uuid].count() < count )
        return;

    QByteArray json_data;

#if OPEN_PROGRESS
    int recvsize = 0;
#endif

    for ( int i = 0; i < count; i++ )
    {
        QString status = mDataCache[uuid][i]["status"].getValue().toString();
        //高速收包，工作很稳定
        if ( status != "ready" )
        {
            //Why? 分散包 count对了，数据却不全，丢弃。
            p4line() << uuid << mDataCache[uuid].count() << count << i + 1 << qPrintable ( status )
                     << "中间丢包，在结束的时候，很多前边包的buffer丢失了。" ;
            mDataCache.remove ( uuid );
            return;
        }

        QByteArray bytes0 = mDataCache[uuid][i]["data"].getValue().toByteArray();
        p4line() << uuid << count << i + 1 << bytes0.size() << bytes0.length()
                 << mDataCache[uuid][i]["data"].getValue().toByteArray().size()
                 << mDataCache[uuid][i]["data"].getValue().toByteArray().length();
        //p4line() << qPrintable ( bytes0 );
        json_data.append ( bytes0 );

#if OPEN_PROGRESS
        recvsize += size;
        updateRecvBytes ( recvsize, totalsize );
        if ( totalsize > 0 )
        {
            int progress = qRound ( 100.0 * recvsize / totalsize );
            updateRecvProgress ( progress );
        }
        //提高接收成功率。
        QQtSleep ( 1 );
#endif
    }

    mDataCache.remove ( uuid );

    //p4line() << qPrintable ( json_data );
    p4line() << "totalsize:" << json_data.size() << json_data.length();

#if OPEN_COMPRESS
    switch ( mCompressMethod )
    {
        case GZipCompress:
        case ZlibCompress:
            json_data = qUncompress ( json_data );
            break;
        case NoCompress:
        default:
            break;
    }
#endif

#if OPEN_ENCODE
    switch ( mTranslateDataEncode )
    {
        case BASE64_DATA:
            json_data = QByteArray::fromBase64 ( json_data );
            break;
        case HEX_DATA:
            json_data = QByteArray::fromHex ( json_data );
            break;
        case RAW_DATA:
        default:
            break;
    }
#else
    //这个经过测试，不行。
    //json_data = QTextCodec::codecForName ( "UTF8" )->fromUnicode ( QString ( json_data ) );
#endif

#if 0
    //单次数据总量 < 64KB - 16字节， 60000字节
    //<4GB， 如果大于4GB，QQtDataSerilization里的packer()函数，size字段会出现越界问题。
    quint32 header = 0x00;
    quint32 size = 0;
    quint32 checksum = 0;
    quint32 tail = 0x00;

    QByteArray bb1 = json_data.left ( 8 ) + json_data.right ( 8 );
    bb1 >> header >> size >> checksum >> tail;
    p4line() << hex << header
             << oct << size
             << hex << checksum
             << hex << tail;
#endif

    QQtLongDataSerialization ds1;
#if OPEN_TRANSLATE_BUFFER
    ds1.buffer() = json_data;
    ds1.unserialize(); //反序列化好
#else
    ds1.dictionary().fromJson ( json_data ); //ds1.serialize();//计算bytes的字节数据。buffer闲置。
#endif
    //ds1.dump();
    //ds1.dump_dictionary();
    recvMessage ( ds1 );
}

void QQtLongDataProtocol::clearDataCache()
{
    if ( mDataCache.size() <= 0 )
        return;

    QStringList list1;

    QQtDictionaryMapIterator itor ( mDataCache.getMap() );
    while ( itor.hasNext() )
    {
        itor.next();
        const QString& uuid = itor.key();
        const QQtDictionary& value = itor.value();

        //get count
        int count  = 1;
        for ( int i = 0; i < value.size(); i++ )
        {
            int tempcount = value[i]["count"].getValue().toInt();
            if ( tempcount > 0 )
            {
                count = tempcount;
                break;
            }
        }

        //count 很全。
        if ( value.size() >= count )
        {
            //不能动？
            for ( int i = 0; i < value.size(); i++ )
            {
                if ( value[i]["status"].getValue().toString() != "ready" )
                {
                    //能动。
                    list1.push_back ( uuid );
                    p4line() << uuid << value.size() << count << i + 1 << "count很全，可是没有全部ready，废弃";
                    break;
                }
            }
        }

        //count 不全。可以直接丢弃？万一是中途的，怎么办！不可以，比较时间戳。
        qint64 curts = QDateTime::currentMSecsSinceEpoch() / 1000;
        for ( int i = 0; i < value.size(); i++ )
        {
            qint64 timestamp = value[i]["timestamp"].getValue().toLongLong();
            if ( ( curts - timestamp ) >= ( m_CleanTimer->interval() / 1000 ) )
            {
                //异常包，包内部一个index里保存的时间戳距离现在超过了30s了，一定是异常的包。
                list1.push_back ( uuid );
                p4line() << uuid << value.size() << count << i + 1 << "时间戳超时太久，丢弃";
                break;
            }
            //时间戳到现在<30s的认为是正常的包。
        }
    }

    //clear illgal
    QListIterator<QString> itor1 ( list1 );
    while ( itor1.hasNext() )
    {
        const QString& uuid = itor1.next();
        mDataCache.remove ( uuid );
    }

    p4line() << "数据Cache的Size：" << mDataCache.size();
}

void QQtLongDataProtocol::slotTimeout()
{
    mLock.lock();
    mRecvFlag = false;
    mLock.unlock();
}

void QQtLongDataProtocol::slotCleanTimeout()
{
    //10-30s, 闲时清理。
    mLock.lock();
    if ( mRecvFlag == false )
        clearDataCache();
    mLock.unlock();
}

bool QQtLongDataProtocol::dispatcher ( const QByteArray& _l )
{
#if 0
    QQtLongDataSerialization ds1;
    ds1.buffer() = _l;
    ds1.unserialize();
    recvMessage ( ds1 );
#else
    mLock.lock();
    mRecvFlag = true;
    mLock.unlock();

    QQtLongDataSerialization ds1;
    ds1.buffer() = _l;
    ds1.unserialize();
    protectedRecvMessage ( ds1 );
#endif
}
